using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

public class MSAA
{
    public int mode;
}
public class Blur
{
    public Material material=new Material(Shader.Find("Hidden/VPR/Blur"));
    
    public readonly int BlurRadius = Shader.PropertyToID("_BlurRadius");
    public float _BlurRadius;
    public int downSample;
    public int iteration;
}

public class Bloom
{
    public Material material=new Material(Shader.Find("Hidden/VPR/Bloom"));
    public readonly int BlurRadius = Shader.PropertyToID("_BlurRadius");
    public float _BlurRadius;
    public float _Intensity;
    public int downSample;
    public int iteration;
    public float luminanceThreshold;
}
public class VBlitPass: VScriptableRenderPass
{
    RenderTextureDescriptor m_Descriptor;
    PostFXSettings settings;
    private Blur m_blur=new Blur();
    
    private bool blurEnble=false;
    private Bloom m_bloom = new Bloom();
    private bool bloomEnble=false;
    private bool hasPostprocess = false;
    public bool IsActive => settings != null;
    GraphicsFormat m_DefaultHDRFormat;
    
    const string k_RenderPostProcessingTag = "Render PostProcessing Effects";
    const string k_RenderFinalPostProcessingTag = "Render Final PostProcessing Pass";
    private static readonly ProfilingSampler m_ProfilingRenderPostProcessing = new ProfilingSampler(k_RenderPostProcessingTag);
    private static readonly ProfilingSampler m_ProfilingRenderFinalPostProcessing = new ProfilingSampler(k_RenderFinalPostProcessingTag);
    private const int k_MaxPyramidSize=16;
    static class ShaderConstants
    {
        public static readonly int _PongTexture = Shader.PropertyToID("_PongTexture");
        public static readonly int _PingTexture = Shader.PropertyToID("_PingTexture");
        public static readonly int _SourceTexLowMip = Shader.PropertyToID("_SourceTexLowMip");
        public static int[] _BloomMipUp;
        public static int[] _BloomMipDown;
    }
    
    public readonly int _SourceTex = Shader.PropertyToID("_SourceTex");
    
    public readonly int BlurDown = Shader.PropertyToID("BlurDown");
    public readonly int BlurUp = Shader.PropertyToID("BlurUp");
    
 
    
    private static readonly int _BlitTex = Shader.PropertyToID("_BlitTex");
    private static readonly int _debug = Shader.PropertyToID("_DebugTex");
    private Material _blitMaterial;
    private RenderTargetIdentifier _source;
    private RenderTargetIdentifier _temp;
    private RenderTargetIdentifier _target;
    RenderTextureDescriptor GetCompatibleDescriptor()
        => GetCompatibleDescriptor(m_Descriptor.width, m_Descriptor.height, m_Descriptor.graphicsFormat);

    RenderTextureDescriptor GetCompatibleDescriptor(int width, int height, GraphicsFormat format, int depthBufferBits = 0)
    {
        var desc = m_Descriptor;
        desc.depthBufferBits = depthBufferBits;
        desc.msaaSamples = 1;
        desc.width = width;
        desc.height = height;
        desc.graphicsFormat = format;
        return desc;
    }
    public VBlitPass(RenderPassEvent renderPassEvent):base(renderPassEvent)
    {
        _blitMaterial = new Material(Shader.Find("Hidden/blit"));
        
        ShaderConstants._BloomMipUp = new int[k_MaxPyramidSize];
        ShaderConstants._BloomMipDown = new int[k_MaxPyramidSize];

        for (int i = 0; i < k_MaxPyramidSize; i++)
        {
            ShaderConstants._BloomMipUp[i] = Shader.PropertyToID("_BloomMipUp" + i);
            ShaderConstants._BloomMipDown[i] = Shader.PropertyToID("_BloomMipDown" + i);
        }
    }
//in RenderTextureDescriptor baseDescriptor
    public void Config(RenderTargetIdentifier source,RenderTargetIdentifier temp)
    {
        _source = source;
        _temp = temp;
        _target = BuiltinRenderTextureType.CameraTarget;
    }
    
    public bool Setup(RenderTextureDescriptor cameraTargetDescriptor,PostFXSettings postsettings)
    {
        m_Descriptor = cameraTargetDescriptor;
        settings = postsettings;
        m_DefaultHDRFormat= QualitySettings.activeColorSpace == ColorSpace.Linear
            ? GraphicsFormat.R8G8B8A8_SRGB : GraphicsFormat.R8G8B8A8_UNorm;
        if (IsActive)
        {
            blurEnble = postsettings.Blur.Enable;
            bloomEnble = postsettings.Bloom.Enable;
            
            hasPostprocess |= blurEnble;
            hasPostprocess |= bloomEnble;
            CommandBuffer cmd = CommandBufferPool.Get();
            if (blurEnble)
            {
                ref var blur = ref postsettings.Blur;
                m_blur._BlurRadius = blur.BlurRadius;
                m_blur.downSample = blur.downSample;
                m_blur.iteration = blur.iteration;
                

            }

            if (bloomEnble)
            {
                ref var bloom = ref postsettings.Bloom;
                m_bloom.iteration = bloom.maxIterations;
                m_bloom.luminanceThreshold = bloom.luminanceThreshold;
                m_bloom._BlurRadius = bloom.BlurRadius;
                m_bloom.downSample = bloom.downscaleLimit;
                m_bloom._Intensity = bloom.Intensity;
            }

            
            

        }
        return true;
    }

    public override void OnCameraCleanup(CommandBuffer cmd)
    {
        if (IsActive && hasPostprocess)
        {
            if (blurEnble)
            {
                cmd.ReleaseTemporaryRT(BlurDown);
                cmd.ReleaseTemporaryRT(BlurUp);
            }

            if (bloomEnble)
            {
                
            }
            
        }
    }

    public override void Execute(ScriptableRenderContext context, ref VRenderingData renderingData)
    {
        CommandBuffer cmd = CommandBufferPool.Get();
        using (new ProfilingScope(cmd, m_ProfilingRenderPostProcessing))
        {
            
            if (IsActive&&hasPostprocess)
            {
                if (blurEnble)
                {
                    
                    cmd.GetTemporaryRT(BlurDown,GetCompatibleDescriptor(
                        m_Descriptor.width,m_Descriptor.height
                                    ,m_DefaultHDRFormat), FilterMode.Bilinear);
                    cmd.GetTemporaryRT(BlurUp,GetCompatibleDescriptor(
                        m_Descriptor.width,m_Descriptor.height
                                    ,m_DefaultHDRFormat), FilterMode.Bilinear);
                    cmd.Blit(_source,BlurDown);
                    BlurRender(cmd, ref renderingData);
                    cmd.Blit(BlurDown,_source);
                }

                if (bloomEnble)
                {
                    
                    cmd.GetTemporaryRT(ShaderConstants._BloomMipDown[0],GetCompatibleDescriptor(m_Descriptor.width,m_Descriptor.height
                        ,m_DefaultHDRFormat), FilterMode.Bilinear);
                    cmd.GetTemporaryRT(ShaderConstants._BloomMipUp[0],GetCompatibleDescriptor(m_Descriptor.width,m_Descriptor.height
                        ,m_DefaultHDRFormat), FilterMode.Bilinear);
                    
                    BloomRender(cmd, ref renderingData);
                    cmd.ReleaseTemporaryRT(ShaderConstants._BloomMipDown[0]); 
                    cmd.ReleaseTemporaryRT(ShaderConstants._BloomMipUp[0]);
                    
                }
                
                
                
                
                context.ExecuteCommandBuffer(cmd);
                
            }
            ConfigFinalPass();
        
        
            FinalPass(context, cmd);
            
        }
        context.ExecuteCommandBuffer(cmd);
        CommandBufferPool.Release(cmd);
    }

    void BloomRender(CommandBuffer cmd, ref VRenderingData renderingData)
    {
        //_LuminanceThreshold
        cmd.SetGlobalTexture(_SourceTex,_source);
        m_bloom.material.SetFloat("_LuminanceThreshold",m_bloom.luminanceThreshold);
        cmd.Blit(
            _source,ShaderConstants._BloomMipDown[0],m_bloom.material,
            0);
        int wh = m_Descriptor.width;
        int hh = m_Descriptor.height;
        int maxSize = Mathf.Max(wh, hh);
        int iterations = Mathf.FloorToInt(Mathf.Log(maxSize, 2f) - 1);
        int mipCount = Mathf.Clamp(m_bloom.iteration, 2, iterations);
        mipCount = Mathf.Clamp(mipCount, 2, k_MaxPyramidSize);
        int lastDown = ShaderConstants._BloomMipDown[0];
        
        Vector4 radio = new Vector4(m_bloom._BlurRadius,0 ,0,m_bloom._BlurRadius);
        for (int i = 1; i < mipCount; i++)
        {
            int mipDown = ShaderConstants._BloomMipDown[i];
            int mipUp = ShaderConstants._BloomMipUp[i];
            wh /= 2;
            hh /= 2;
            cmd.GetTemporaryRT(mipDown,GetCompatibleDescriptor(wh,hh,m_DefaultHDRFormat),FilterMode.Bilinear);
            cmd.GetTemporaryRT(mipUp,GetCompatibleDescriptor(wh,hh,m_DefaultHDRFormat),FilterMode.Bilinear);
            
            cmd.SetGlobalTexture(_SourceTex,lastDown);
            m_bloom.material.SetVector(m_bloom.BlurRadius,radio);
            cmd.Blit(lastDown,mipUp,m_bloom.material,1);
            
            cmd.SetGlobalTexture(_SourceTex,mipUp);
            m_bloom.material.SetVector(m_bloom.BlurRadius,radio);
            cmd.Blit(mipUp,mipDown,m_bloom.material,2);
            
            lastDown = mipDown;
            
        }
        
        for (int i = mipCount-2; i >= 0; i--)
        {
            //128
            int lowMip = (i == mipCount - 2) ? ShaderConstants._BloomMipDown[i + 1] : ShaderConstants._BloomMipUp[i + 1];
            
            //256
            int highMip = ShaderConstants._BloomMipDown[i];
            int dst = ShaderConstants._BloomMipUp[i];
            
            cmd.SetGlobalTexture(_SourceTex,highMip);
            cmd.SetGlobalTexture(ShaderConstants._SourceTexLowMip,lowMip);
            m_bloom.material.SetVector(m_bloom.BlurRadius,radio);
            cmd.Blit(highMip,dst,m_bloom.material,3);
            
        }
        cmd.SetGlobalTexture("_Bloom",ShaderConstants._BloomMipUp[0]);
        cmd.SetGlobalTexture("_SourceTex",_source);
        m_bloom.material.SetFloat("_Intensity",m_bloom._Intensity);
       
        
        cmd.Blit(_source,ShaderConstants._BloomMipDown[0], m_bloom.material ,4);
        
        cmd.Blit(ShaderConstants._BloomMipDown[0],_source);
        // Cleanup
        for (int i = 1; i < mipCount; i++)
        {
            cmd.ReleaseTemporaryRT(ShaderConstants._BloomMipDown[i]); 
            cmd.ReleaseTemporaryRT(ShaderConstants._BloomMipUp[i]);
        }
    }
    void BlurRender(CommandBuffer cmd, ref VRenderingData renderingData)
    {
        int wh = m_Descriptor.width;
        int hh = m_Descriptor.height;
        Vector4 radio = new Vector4(m_blur._BlurRadius,0 ,0,m_blur._BlurRadius);
        for (int i = 0; i < m_blur.iteration; i++)
        {
            cmd.SetGlobalTexture(_SourceTex,BlurDown);
            m_blur.material.SetVector(m_blur.BlurRadius,radio);
            cmd.Blit(BlurDown,BlurUp,m_blur.material,0);
            
            cmd.SetGlobalTexture(_SourceTex,BlurUp);
            m_blur.material.SetVector(m_blur.BlurRadius,radio);
            cmd.Blit(BlurUp,BlurDown,m_blur.material,1);
            
            cmd.ReleaseTemporaryRT(BlurUp);
            wh /= 2;
            hh /= 2;
            
            cmd.GetTemporaryRT(BlurUp,GetCompatibleDescriptor(wh,hh,m_DefaultHDRFormat),FilterMode.Bilinear);
            cmd.Blit(BlurDown,BlurUp);
            cmd.ReleaseTemporaryRT(BlurDown);
            cmd.GetTemporaryRT(BlurDown,GetCompatibleDescriptor(wh,hh,m_DefaultHDRFormat),FilterMode.Bilinear);
            cmd.Blit(BlurUp,BlurDown);
        }
        
        for (int i = 0; i < m_blur.iteration; i++)
        {
            cmd.SetGlobalTexture(_SourceTex,BlurDown);
            m_blur.material.SetVector(m_blur.BlurRadius,radio);
            cmd.Blit(BlurDown,BlurUp,m_blur.material,0);
            
            cmd.SetGlobalTexture(_SourceTex,BlurUp);
            m_blur.material.SetVector(m_blur.BlurRadius,radio);
            cmd.Blit(BlurUp,BlurDown,m_blur.material,1);
            
            cmd.ReleaseTemporaryRT(BlurUp);
            wh *= 2;
            hh *= 2;
            
            cmd.GetTemporaryRT(BlurUp,GetCompatibleDescriptor(wh,hh,m_DefaultHDRFormat),FilterMode.Bilinear);
            cmd.Blit(BlurDown,BlurUp);
            cmd.ReleaseTemporaryRT(BlurDown);
            cmd.GetTemporaryRT(BlurDown,GetCompatibleDescriptor(wh,hh,m_DefaultHDRFormat),FilterMode.Bilinear);
            cmd.Blit(BlurUp,BlurDown);
        }
        
        
        
    }
    private void ConfigFinalPass()
    {
        _source =  _source;
        _target = BuiltinRenderTextureType.CameraTarget;
    }
    private void FinalPass(ScriptableRenderContext context,CommandBuffer cmd)
    {
        
        cmd.Clear();
       
       
        
        cmd.SetGlobalTexture(_BlitTex,_source);
        
        cmd.Blit(_source,_target,_blitMaterial);
        
      
    }
}
